using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.Data;
using System.Data.SqlClient;

namespace System.Web.Core
{
    public class SqlMemberProvider : MemberProvider
    {
        #region Private Members

        private string connectionString;
        private int passwordFormat;

        #endregion

        #region Override Methods

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            base.Initialize(name, config);

            string connectionStringName = config["connectionStringName"];
            if (connectionStringName == null || connectionStringName.Length < 1)
            {
                throw new ProviderException("Connection name not specified.");
            }
            connectionString = ConfigurationManager.AppSettings[connectionStringName];
            if (connectionString == null || connectionString.Length < 1)
            {
                throw new ProviderException("Connection string not found.");
            }

            string strTemp = config["passwordFormat"];
            if (strTemp == null)
            {
                strTemp = "Hashed";
            }
            switch (strTemp)
            {
                case "Clear":
                    passwordFormat = 0;
                    break;
                case "Hashed":
                    passwordFormat = 1;
                    break;
                case "Encrypted":
                    passwordFormat = 2;
                    break;
                default:
                    throw new ProviderException("Invalid password format.");
            }
        }

        public override Member GetMember(string memberName)
        {
            using (SqlConnection connection = GetSqlConnection())
            {
                SqlCommand command = new SqlCommand("sp_GetMember", connection);
                SqlDataReader dr = null;

                command.CommandType = CommandType.StoredProcedure;

                command.Parameters.Add("@MemberName", SqlDbType.VarChar).Value = memberName;

                connection.Open();
                dr = command.ExecuteReader();
                Member member = null;

                if (dr.Read())
                {
                    member = PopulateMemberFromIentityReader(dr, dr.GetSchemaTable());
                }

                dr.Close();
                connection.Close();

                return member;
            }
        }
        public override List<Member> GetMembers()
        {
            using (SqlConnection connection = GetSqlConnection())
            {
                SqlCommand command = new SqlCommand("sp_GetMembers", connection);
                SqlDataReader dr = null;

                command.CommandType = CommandType.StoredProcedure;

                connection.Open();
                dr = command.ExecuteReader();
                List<Member> members = new List<Member>();
                DataTable schemaTable = dr.GetSchemaTable();

                while (dr.Read())
                {
                    Member member = PopulateMemberFromIentityReader(dr, schemaTable);
                    if (member != null)
                    {
                        members.Add(member);
                    }
                }

                dr.Close();
                connection.Close();

                return members;
            }
        }
        public override List<Member> GetMembersByMemberId(Guid memberId)
        {
            using (SqlConnection connection = GetSqlConnection())
            {
                SqlCommand command = new SqlCommand("sp_GetMembersByMemberId", connection);
                SqlDataReader dr = null;

                command.CommandType = CommandType.StoredProcedure;

                command.Parameters.Add("@MemberId", SqlDbType.UniqueIdentifier).Value = memberId;

                connection.Open();
                dr = command.ExecuteReader();
                List<Member> members = new List<Member>();
                DataTable schemaTable = dr.GetSchemaTable();

                while (dr.Read())
                {
                    Member member = PopulateMemberFromIentityReader(dr, schemaTable);
                    if (member != null)
                    {
                        members.Add(member);
                    }
                }

                dr.Close();
                connection.Close();

                return members;
            }
        }
        public override void CreateMember(Member member)
        {
            if (member == null)
            {
                return;
            }

            member.PasswordFormat = passwordFormat;
            member.PasswordSalt = GenerateSalt();
            member.Password = EncodePassword(member.Password, passwordFormat, member.PasswordSalt);

            using (SqlConnection connection = GetSqlConnection())
            {
                SqlCommand command = new SqlCommand("sp_CreateMember", connection);

                command.CommandType = CommandType.StoredProcedure;

                SqlParameterCollection parameters = command.Parameters;
                SqlParameter parameter = null;

                parameter = new SqlParameter("@ReturnValue", SqlDbType.Int);
                parameter.Direction = ParameterDirection.ReturnValue;
                parameters.Add(parameter);

                parameter = new SqlParameter("@MemberId", SqlDbType.UniqueIdentifier);
                parameter.Value = member.MemberId;
                parameters.Add(parameter);

                parameter = new SqlParameter("@MemberName", SqlDbType.VarChar);
                parameter.Value = member.MemberName;
                parameters.Add(parameter);

                parameter = new SqlParameter("@Password", SqlDbType.NVarChar);
                parameter.Value = member.Password;
                parameters.Add(parameter);

                parameter = new SqlParameter("@PasswordFormat", SqlDbType.Int);
                parameter.Value = member.PasswordFormat;
                parameters.Add(parameter);

                parameter = new SqlParameter("@PasswordSalt", SqlDbType.NVarChar);
                parameter.Value = member.PasswordSalt;
                parameters.Add(parameter);

                connection.Open();
                command.ExecuteNonQuery();
                connection.Close();

                if ((int)command.Parameters["@ReturnValue"].Value == 0)
                {
                    throw new Exception(string.Format("Member '{0}' is exist already.", member.MemberName));
                }
            }
        }
        public override void DeleteMember(Guid memberId, string memberName)
        {
            if (memberId == Guid.Empty && string.IsNullOrEmpty(memberName))
            {
                return;
            }

            using (SqlConnection connection = GetSqlConnection())
            {
                SqlCommand command = new SqlCommand("sp_DeleteMember", connection);
                SqlParameter parameter = null;

                command.CommandType = CommandType.StoredProcedure;

                parameter = new SqlParameter("@MemberId", SqlDbType.UniqueIdentifier);
                if (memberId == Guid.Empty)
                {
                    parameter.Value = DBNull.Value;
                }
                else
                {
                    parameter.Value = memberId;
                }
                command.Parameters.Add(parameter);

                parameter = new SqlParameter("@MemberName", SqlDbType.VarChar);
                if (string.IsNullOrEmpty(memberName))
                {
                    parameter.Value = DBNull.Value;
                }
                else
                {
                    parameter.Value = memberName;
                }
                command.Parameters.Add(parameter);

                connection.Open();
                command.ExecuteNonQuery();
                connection.Close();
            }
        }
        public override void ChangeMemberPassword(Member member, string newPassword)
        {
            if (member == null || string.IsNullOrEmpty(newPassword))
            {
                return;
            }

            using (SqlConnection connection = GetSqlConnection())
            {
                SqlCommand command = new SqlCommand("sp_UpdateMemberPassword", connection);
                SqlParameter parameter = null;
                string encryptPassword = EncodePassword(newPassword, member.PasswordFormat, member.PasswordSalt);

                command.CommandType = CommandType.StoredProcedure;

                parameter = new SqlParameter("@MemberId", SqlDbType.UniqueIdentifier);
                parameter.Value = member.MemberId;
                command.Parameters.Add(parameter);

                parameter = new SqlParameter("@Password", SqlDbType.NVarChar);
                parameter.Value = encryptPassword;
                command.Parameters.Add(parameter);

                connection.Open();
                command.ExecuteNonQuery();
                connection.Close();

                member.Password = encryptPassword;
            }
        }
        public override string ResetMemberPassword(Member member)
        {
            if (member == null)
            {
                return null;
            }

            string newPassword = GeneratePassword(8);
            ChangeMemberPassword(member, newPassword);

            return newPassword;
        }
        public override bool ValidateMember(Guid memberId, string password)
        {
            if (memberId == Guid.Empty)
            {
                return false;
            }

            List<Member> members = GetMembersByMemberId(memberId);
            if (members.Count == 0)
            {
                return false;
            }

            using (SqlConnection connection = GetSqlConnection())
            {
                SqlCommand command = new SqlCommand("sp_ValidateMember", connection);
                SqlParameter parameter = null;

                command.CommandType = CommandType.StoredProcedure;

                parameter = new SqlParameter("@ReturnValue", SqlDbType.Int);
                parameter.Direction = ParameterDirection.ReturnValue;
                command.Parameters.Add(parameter);

                parameter = new SqlParameter("@MemberId", SqlDbType.UniqueIdentifier);
                parameter.Value = memberId;
                command.Parameters.Add(parameter);

                parameter = new SqlParameter("@MemberName", SqlDbType.VarChar);
                parameter.Value = DBNull.Value;
                command.Parameters.Add(parameter);

                parameter = new SqlParameter("@Password", SqlDbType.NVarChar);
                parameter.Value = EncodePassword(password, members[0].PasswordFormat, members[0].PasswordSalt);
                command.Parameters.Add(parameter);

                connection.Open();
                command.ExecuteNonQuery();
                connection.Close();

                if ((int)command.Parameters["@ReturnValue"].Value == 0)
                {
                    return false;
                }

                return true;
            }
        }
        public override bool ValidateMember(string memberName, string password)
        {
            if (string.IsNullOrEmpty(memberName) || string.IsNullOrEmpty(password))
            {
                return false;
            }

            Member member = GetMember(memberName);
            if (member == null)
            {
                return false;
            }

            using (SqlConnection connection = GetSqlConnection())
            {
                SqlCommand command = new SqlCommand("sp_ValidateMember", connection);
                SqlParameter parameter = null;

                command.CommandType = CommandType.StoredProcedure;

                parameter = new SqlParameter("@ReturnValue", SqlDbType.Int);
                parameter.Direction = ParameterDirection.ReturnValue;
                command.Parameters.Add(parameter);

                parameter = new SqlParameter("@MemberId", SqlDbType.UniqueIdentifier);
                parameter.Value = DBNull.Value;
                command.Parameters.Add(parameter);

                parameter = new SqlParameter("@MemberName", SqlDbType.VarChar);
                parameter.Value = memberName;
                command.Parameters.Add(parameter);

                parameter = new SqlParameter("@Password", SqlDbType.NVarChar);
                parameter.Value = EncodePassword(password, member.PasswordFormat, member.PasswordSalt);
                command.Parameters.Add(parameter);

                connection.Open();
                command.ExecuteNonQuery();
                connection.Close();

                if ((int)command.Parameters["@ReturnValue"].Value == 0)
                {
                    return false;
                }

                return true;
            }
        }

        #endregion

        #region Private Methods

        private SqlConnection GetSqlConnection()
        {
            return new SqlConnection(connectionString);
        }
        private bool ColumnExistAndHasValue(IDataReader dr, DataTable schemaTable, string columnName)
        {
            if (string.IsNullOrEmpty(columnName) || schemaTable == null)
            {
                return false;
            }
            DataColumn col = schemaTable.Columns[0];
            foreach (DataRow row in schemaTable.Rows)
            {
                if (row[col].ToString().ToLower() == columnName.ToLower() && dr[columnName.ToLower()] != DBNull.Value)
                {
                    return true;
                }
            }
            return false;
        }
        private Member PopulateMemberFromIentityReader(IDataReader dr, DataTable schemaTable)
        {
            Member member = new Member();

            if (ColumnExistAndHasValue(dr, schemaTable, "MemberId"))
            {
                member.MemberId = new Guid(dr["MemberId"].ToString());
            }
            if (ColumnExistAndHasValue(dr, schemaTable, "MemberName"))
            {
                member.MemberName = dr["MemberName"].ToString();
            }
            if (ColumnExistAndHasValue(dr, schemaTable, "Password"))
            {
                member.Password = dr["Password"].ToString();
            }
            if (ColumnExistAndHasValue(dr, schemaTable, "PasswordFormat"))
            {
                member.PasswordFormat = int.Parse(dr["PasswordFormat"].ToString());
            }
            if (ColumnExistAndHasValue(dr, schemaTable, "PasswordSalt"))
            {
                member.PasswordSalt = dr["PasswordSalt"].ToString();
            }

            return member;
        }
        public string GeneratePassword(int length)
        {
            List<string> items = new List<string>();
            Random r = new Random();
            for (int i = 0; i < length; i++)
            {
                items.Add(r.Next(0, 10).ToString());
            }
            return string.Join("", items.ToArray());
        }

        #endregion
    }
}